﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Networking.Sockets;
using Windows.Storage;
using Windows.Storage.Streams;
using System.Runtime.InteropServices.WindowsRuntime;


namespace Eu.Xquirrel.Networking.Http.Offline
{
    public class OfflineHttpServer : IDisposable
    {
        private List<StreamSocketListener> _listenerList = new List<StreamSocketListener>();
        private Dictionary<string, string> _mimes = new Dictionary<string, string>();
        private static readonly StorageFolder _localFolder = KnownFolders.PicturesLibrary;
        private string _rootFolder;
        private int _port;
        private const uint BufferSize = 1024 * 8;

        public OfflineHttpServer(string rootFolder, int port)
        {
            _rootFolder = rootFolder;
            _port = port;
            PopulateMimes();
            CreateNewListener(port);
        }

        public OfflineHttpServer(StorageFolder folder, int port)
        {
            string[] myfolder = folder.Path.Replace("\\\\", "\\").Split('\\');

            bool start = false;
            string path = string.Empty;
            for (int x = 0; x < myfolder.Length; x++)
            {
                if (start)
                {
                    path += "\\"+myfolder[x];
                }                
                if (myfolder[x].ToLower() == "pictures")
                {
                    start = true;
                }
            }
            _rootFolder = path.Substring(1);
            _port = port;
            PopulateMimes();
            CreateNewListener(port);
        }

        private async void PopulateMimes()
        {
            var mimesTypes = new MimeTypes();
            _mimes = await mimesTypes.GetMimeList();
        }

        private void CreateNewListener(int port)
        {
            var listener = new StreamSocketListener();
            _listenerList.Add(listener);

            listener.ConnectionReceived += (s, e) =>
            {
                ProcessConnectionRequestAsync(listener, e.Socket);
            };
            listener.BindServiceNameAsync(port.ToString());
        }

        public void Dispose()
        {
            foreach (var listener in _listenerList)
            {
                listener.Dispose();
            }
        }

        private async void ProcessConnectionRequestAsync(StreamSocketListener listener, StreamSocket socket)
        {
            CreateNewListener(_port);

            StringBuilder request = new StringBuilder();
            using (IInputStream input = socket.InputStream)
            {
                byte[] data = new byte[BufferSize];
                IBuffer buffer = data.AsBuffer();
                uint dataRead = BufferSize;
                while (dataRead == BufferSize)
                {
                    await input.ReadAsync(buffer, BufferSize, InputStreamOptions.Partial);
                    request.Append(Encoding.UTF8.GetString(data, 0, data.Length));
                    dataRead = buffer.Length;
                }
            }

            using (IOutputStream output = socket.OutputStream)
            {
                string requestMethod = request.ToString().Split('\n')[0];
                string[] requestParts = requestMethod.Split(' ');

                if (requestParts[0] == "GET")
                    try
                    {
                        await WriteResponseAsync(requestParts[1], output);
                    }
                    catch (Exception ex)
                    {

                    }
                else
                    throw new InvalidDataException("HTTP method not supported: "
                                                   + requestParts[0]);

            }

        }

        private async Task WriteResponseAsync(string path, IOutputStream os)
        {
            using (Stream resp = os.AsStreamForWrite())
            {

                var pathSplit = path.Split('.');
                string extension = "." + pathSplit[pathSplit.Count() - 1];



                string contentType = string.Empty;
                if (_mimes.ContainsKey(extension))
                {
                    contentType = _mimes[extension];
                }

                bool exists = true;
                try
                {

                    string filePath = _rootFolder + path.Replace('/', '\\');
                    using (Stream fs = await _localFolder.OpenStreamForReadAsync(filePath))
                    {
                        string header = String.Format("HTTP/1.1 200 OK\r\n" +
                                        "Content-Type: {0}\r\n" +
                                        "Content-Length: {1}\r\n" +
                                        "Connection: close\r\n\r\n",
                                        contentType,
                                        fs.Length
                                        );

                        byte[] headerArray = Encoding.UTF8.GetBytes(header);
                        await resp.WriteAsync(headerArray, 0, headerArray.Length);

                        await fs.CopyToAsync(resp);
                    }
                }
                catch (FileNotFoundException)
                {
                    exists = false;
                }

                if (!exists)
                {
                    byte[] headerArray = Encoding.UTF8.GetBytes(
                                          "HTTP/1.1 404 Not Found\r\n" +
                                          "Content-Length:0\r\n" +
                                          "Connection: close\r\n\r\n");
                    await resp.WriteAsync(headerArray, 0, headerArray.Length);
                }

                await resp.FlushAsync();
            }
        }
    }
}

